features:
  - id: f1
    feature: A decline returns a typed error and never creates an order row
    kind: step
    implemented: The service maps the provider's decline code to a typed error and
      skips order creation — repo://payments/src/charges.ts ·
      api://payments/charges
    gap: —
    owner: "[Nadia Ilic](</Team/Nadia Ilic.md>)"
    tests: test://payments/declines.test.ts
    uuid: 702318cc-00c0-44f6-9bd6-4b38c0f23fd3
  - id: f2
    feature: The checkout keeps the cart and shows the decline reason with a retry
      affordance
    kind: ux
    implemented: The error state renders the reason and a retry button over the
      intact cart — repo://storefront/src/pages/checkout.tsx
    gap: Hard declines (fraud codes) still show the retry affordance the flow says
      they should not offer — see q1
    owner: "[Nadia Ilic](</Team/Nadia Ilic.md>)"
    tests: —
    uuid: 19b504a7-2b51-432b-8578-fe1fc9878119
scenarios:
  - id: s1
    scenario: Soft decline (insufficient funds) — buyer retries with another card,
      second attempt succeeds
    kind: unhappy
    outcome: second charge attempt under a new idempotency key creates the order
    result: order placed
    tests: test://payments/declines.test.ts
    uuid: d8a4a9f9-304d-4d4d-90d2-86026047eb4b
  - id: s2
    scenario: Hard decline (fraud code) — no retry offered
    kind: unhappy
    outcome: attempt recorded, cart kept, support contact shown
    result: no order
    tests: —
    uuid: 1c58a73a-4b39-42c5-b836-6010d72b0649
open-questions:
  - q: q1
    question: Should hard declines (fraud codes) hide the retry affordance?
    about: f2
    status: open
    options: "Hide it (Recommended) :: a fraud decline never succeeds on retry, so
      the affordance only invites card testing; Keep it :: hiding it leaks which
      declines we treat as fraud — show it and let the provider keep declining"
    uuid: 473c690c-52a4-412f-afa8-b65767061e17
    resolution: ""
    owner: ""
